package server;

import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import java.util.*;

/**
 * SettlersServer
 */
public class SettlersServer {
    private static final int PORT = 10997;
    private static final int CHANNEL_WRITE_SLEEP = 30;


    ServerSocketChannel sSockChan;
    Selector readSelector;
    List<SocketChannel> clients;
    ByteBuffer readBuffer;
   // ByteBuffer writeBuffer;


    public SettlersServer() {
        init();
        initServerSocket();
    }

    private void init() {
        clients = new LinkedList();
//        writeBuffer = ByteBuffer.allocateDirect(255);
        readBuffer = ByteBuffer.allocateDirect(255);
    }

    private void initServerSocket() {
        try {
            sSockChan = ServerSocketChannel.open();
            sSockChan.configureBlocking(false);
            InetAddress addr = InetAddress.getLocalHost();
            sSockChan.socket().bind(new InetSocketAddress(addr, PORT));
            readSelector = Selector.open();
        }
        catch (Exception e) {
            System.err.println("error initializing server" +  e.toString());
        }
    }

    private void acceptNewConnections() {
        try {
            SocketChannel clientChannel;
            while ((clientChannel = sSockChan.accept()) != null) {
                addNewClient(clientChannel);
                sendBroadcastMessage("login from: " +
                        clientChannel.socket().getInetAddress(),
                        clientChannel);
                sendMessage(clientChannel, "\n\nWelcome to Settlers of Catan," +
                        "there are " + clients.size() + " users online.\n");

            }
        }
        catch (Exception ex) {
            System.err.println("error accepting new connections" + ex.toString());
        }
    }

    private void addNewClient(SocketChannel chan) {
        clients.add(chan);
        try {
            chan.configureBlocking(false);
            SelectionKey readKey = chan.register(readSelector,
                    SelectionKey.OP_READ, new StringBuffer());
        }
        catch (Exception e) {}
    }

    private void sendMessage(SocketChannel channel, Object mesg) {
        //prepWriteBuffer(mesg);
        channelWrite(channel, mesg);
    }

    private void sendBroadcastMessage(Object mesg, SocketChannel from) {
//        prepWriteBuffer(mesg);
        Iterator<SocketChannel> i = clients.iterator();
        while (i.hasNext()) {
            SocketChannel channel = i.next();
            if (channel != from) {
                channelWrite(channel, mesg);
            }
        }
    }
/*
    private void prepWriteBuffer(String mesg) {
        writeBuffer.clear();
        writeBuffer.put(mesg.getBytes());
        writeBuffer.putChar('\n');
        writeBuffer.flip();
    }
*/
    private void channelWrite(SocketChannel channel, Object object) {
        try{
            ObjectOutputStream out = new ObjectOutputStream(channel.socket().getOutputStream());
            out.writeObject(object);
            out.flush();
                try {
                    Thread.sleep(CHANNEL_WRITE_SLEEP);
                }
                catch(Exception ex) {}
        }
        catch(Exception k){
           System.out.println(k);
        }
       /*    long nbytes = 0;
        long toWrite = writeBuffer.remaining();

        try {
            while (nbytes != toWrite) {
                nbytes += channel.write(writeBuffer);
                try {
                    Thread.sleep(CHANNEL_WRITE_SLEEP);
                }
                catch(Exception ex) {}
            }

        }
        catch(Exception ex) {}

        writeBuffer.rewind();

     */}

    private void readIncomingMessages() {
        try {
            readSelector.selectNow();
            Set readyKeys = readSelector.selectedKeys();
            Iterator<SelectionKey> i = readyKeys.iterator();
            while (i.hasNext()) {
                SelectionKey key = i.next();
                i.remove();
                SocketChannel channel = (SocketChannel)key.channel();
                readBuffer.clear();
                long nbytes = channel.read(readBuffer);
                if (nbytes == -1) {
                    channel.close();
                    clients.remove(channel);
                    sendBroadcastMessage("logout: " + channel.socket().getInetAddress(), channel);
                }
                else {
                    StringBuffer sb = (StringBuffer)key.attachment();
                    readBuffer.flip();
                    String str = decode(readBuffer).toString();
                    readBuffer.clear();
                    sb.append(str);

                    String line = sb.toString();
                    if ((line.indexOf("\n") != -1) ||
                            (line.indexOf("\r") != -1))
                        line = line.trim();
                    if (line.startsWith("quit")) {
                        channel.close();
                        clients.remove(channel);
                        sendBroadcastMessage("logout: " + channel.socket().getInetAddress(), channel);
                    }
                    else {
                        sendBroadcastMessage(channel.socket().getInetAddress() + ": " + line, channel);
                        sb.delete(0, sb.length());
                    }
                }
            }
        }
        catch (Exception ex) {

    }
    }

    private static String decode(ByteBuffer b) {
        String result = "";
        byte[] bytes = b.array();
        for (int i = 0; i < bytes.length; i++) {
            result += "" + bytes[i];
        }
        return result;
    }
}
